RequestMapping注解
- SpringMVC使用
@RequestMapping
注解为控制器指定可以处理哪些URL请求 - 在控制器的类定义及方法定义处都可标注
- 类定义处:提供初步的请求映射信息,相对于WEB应用的根目录
- 方法定义处:提供进一步的细分映射信息,相对于类定义处的URL,若类定义处未标注
@RequestMapping
,则方法标记处的URL相当于WEB应用的根目录
- DispatchServlet截获请求后,就通过控制器上
@RequestMapping
提供的映射信息确定请求所对应的处理方法。 @RequestMapping
除了可以使用请求URL映射请求外,还可以使用请求方法、请求参数及请求头映射请求@RequestMapping
的value、method、params和heads分别表示请求URL、请求方法、请求参数及请求头的映射条件,它们之间是与的关系,联合使用多个条件可以让请求映射更加精确化- params和headers支持简单的表达式:
- param1:表示请求必须包含名为param1的请求参数
- !param1:表示请求不能包含名为param1的请求参数
- param1!=value1:表示请求包含名为param1的请求参数,但其值不能为value1
- {“param1=value1”, “param2”}:表示请求包含名为param1和param2的两个请求参数,且param1参数的值必须为value1
- Ant风格资源地址支持3种匹配符:
- ?:匹配文件名中的一个字符
- *:匹配文件名中的任意字符
- **:匹配多层路径
@RequestMapping
还支持Ant风格的URL:- /user/*/createUser:匹配/user/aaa/createUser、/user/bbb/createUser等URL
- /user/**/createUser:匹配/user/createUser、/user/aaa/bbb/createUser等URL
- /user/createUser??:匹配/user/createUseraa、/user/createUserbb等URL
@PathVariable
注解
@PathVariable
映射URL绑定的占位符,通过@PathVariable
可以将URL中占位符参数绑定到控制器处理方法的入参中:URL中的{xxx}占位符可以通过@PathVariable("xxx")
绑定到操作方法的入参中。
|
|
REST
HTTP协议里面,四个操作方式的动词:GET、POST、PUT、DELETE,它们分别对应4种基本操作:GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。
示例:
- /order/1 HTTP GET:得到id=1的order
- /order/1 HTTP DELETE:删除id=1的order
- /order/1 HTTP PUT:更新id=1的order
- /order HTTP POST:新增order
浏览器form表单只支持GET和POST请求,而DELETE、PUT等method并不支持,Spring添加了一个过滤器HiddenHttpMethodFilter,可以将这些请求转换为标准的http方法,使得支持GET、POST、PUT与DELETE请求。
需在web.xml中配置HiddenHttpMethodFilter,例如:
|
|
相应的index.jsp如下:
|
|
相应的处理器方法如下:
|
|
使用@RequestParam
绑定请求参数值
在处理方法入参处使用@RequestParam
可以把请求参数传递给请求方法,其具有两个属性:value表示参数名,required表示是否必须,默认为true,表示请求参数中必须包含对应的参数,若不存在,将抛出异常!
|
|
使用POJO对象绑定请求参数值
SpringMVC会按请求参数名和POJO属性名进行自动匹配,自动为该对象填充属性值,支持级联属性!
|
|
相应的表单文件为:
|
|
使用Servlet API作为入参
MVC的Handler方法可以接收以下这些类型的ServletAPI参数:
- HttpServletRequest
- HttpServletResponse
- HttpSession
- java.security.Principal
- Locale
- InputStream
- OutputStream
- Reader
- Writer
|
|
处理模型数据
SpringMVC提供了以下几种方法输出模型数据:
ModelAndView
:处理方法返回值类型为ModelAndView,方法体即可通过该对象添加模型数据Map和Model
:入参为Model、ModelMap或Map时,处理方法返回时,Map中的数据会自动添加到模型中@SessionAttributes
:将模型中的某个属性暂存到HttpSession中,以便多个请求之间可以共享这个属性@ModelAttribute
:方法入参标注该注解后,入参的对象就会放到数据模型中
ModelAndView
控制器处理方法的返回值如果是ModelAndView,则其既包含视图信息,又包含模型数据信息。
- 添加模型数据
ModelAndView addObject(String attributeName, Object attributeValue)
ModelAndView addAllObject(Map<String, ?> modelMap)
- 设置视图
void setView(View view)
void setViewName(String viewName)
|
|
正如上面注释所述,通过源码分析可知,添加到ModelAndView中的数据最终放到到了request请求域中。
Map以及Model
SpringMVC在内部使用了一个Model接口存储模型数据,具体步骤为:
- SpringMVC在调用方法前会创建一个隐含的模型对象作为模型数据的存储数据
- 如果方法的入参为Map或Model类型,SpringMVC会将隐含模型的引用传递给这些入参,在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据。
|
|
@SessionAttributes
若希望在多个请求之间共用某个模型属性数据,则可以在控制器类上标注一个@SessionAttributes
,SpringMVC将在模型中对应的属性存到HttpSession中。
@SessionAttributes
除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中。
@SessionAttributes(types=User.class)
会将隐含模型中所有类型为User.class的属性添加到会话中@SessionAttributes(value={"user1", "user2"})
@SessionAttributes(types={User.class, Dept.class})
@SessionAttributes(value={"user1", "user2"}, types={Dept.class})
|
|
@ModelAttribute
先看下面这个例子:
|
|
SpringMVC确定目标方法POJO类型入参的过程
- 确定一个key
- 若目标方法的POJO类型的参数木有使用
@ModelAttribute
作为修饰,则key为POJO类名第一个字母小写 - 若使用了
@ModelAttribute
来修饰,则key为@ModelAttribute
注解的value属性值
- 若目标方法的POJO类型的参数木有使用
- 在implicitModel中查找key对应的对象,若存在,则作为入参传入
- 若在
@ModelAttribute
标记的方法中在Map中保存过且key和第一步中确定的key一致则会获取到
- 若在
- 若不存在key对应的对象则检查当前的Handler是否使用
@SessionAttributes
注解进行修饰,若使用了该注解,且@SessionAttributes
注解的value属性值中包含了key则会从HttpSession中来获取key所对应的value值,若存在则直接传入到目标方法的入参中,若不存在则将抛出异常 - 若Handler没有标识
@SessionAttributes
注解或@SessionAttributes
注解的value值中不包含key,则会通过反射来创建POJO类型的参数,然后传入为目标方法的参数 - SpringMVC会把key和POJO类型的对象保存到implicitModel中进而会保存到request中
视图和视图解析器
无论目标方法返回时String、ModelAndView或者View,SpringMVC都会转换成ModelAndView对象
SpringMVC借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是JSP,也可能是Excel、JFreeChart等各种表现形式的视图
若项目中使用了JSTL,则SpringMVC会自动把视图由InternalResourceView转为JstlView
若使用JSTL的fmt标签则需要在SpringMVC的配置文件中配置国际化资源文件
123<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"><property name="basename" value="i18n"></property></bean>若希望直接响应通过SpringMVC渲染的页面,不需要经过Handler,可以使用
mvc:view-controller
标签实现1<mvc:view-controller path="testJstlView" view-name="success"/>但是这样配置之后,原来的那些需要经过Handler的链接就将失效,实际上在实际开发中通常需要配置
mvc:annotation-driven
:<mvc:annotation-driven/>
,此时即可解决问题。
自定义视图
自定义视图需要实现View接口,并且需要在Spring的配置文件中配置BeanNameViewResolver,如下:
HelloView
123456789101112public class HelloView implements View {public String getContentType() {return "text/html";}public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {response.getWriter().print("hello view, time: " + new Date());}}HelloView实现View接口,需要实现两个接口,HelloView需要加上
@Component
注解从而注入到IOC容器中,这是因为BeanNameViewResolver需要从IOC容器中获取该View。配置文件的配置
12345<!--配置视图解析器BeanNameViewResolver,使用视图的名字来解析视图--><!--通过order属性来定义视图解析器的优先级,order值越小,优先级越高--><bean class="org.springframework.web.servlet.view.BeanNameViewResolver"><property name="order" value="100"/></bean>测试
12345"/testView")(public String testView() {System.out.println("testView");return "helloView";}需要注意的是testView()的返回值必须返回
"helloView"
,这是因为HelloView在IOC容器的ID为helloView,而BeanNameViewResolver正是通过testView()方法的返回值在IOC容器中查找自定义View的。
关于重定向
- 一般情况下控制器方法的返回字符串类型的值会被当成逻辑视图名处理
- 如果返回的字符串中带
forward:
或redirect:
前缀时,SpringMVC会对他们进行特殊处理,将forward:和redirect:当成指示符,其后的字符串作为URL来处理。redirect:success.jsp
:会完成一个到success.jsp的重定向的操作forward:success.jsp
:会完成一个到success.jsp的转发操作
数据绑定
SpringMVC主框架将ServletRequest对象即目标方法的入参实例传递给WebDataBinderFactory实例,以创建DataBinder实例对象
DataBinder调用装配在SpringMVC上下文的ConversionService组件进行数据类型转换、数据格式化工作,将Servlet中的请求信息填充到入参对象中
调用Validator组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果BingingData对象
SpringMVC抽取BindingResult中的入参对象和校验错误对象,将它们赋给处理方法的响应入参
SpringMVC通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中,数据绑定的核心是DataBinder,运行机制如下:
自定义类型转换器
ConversionService是Spring类型转换体系的核心接口,可以利用ConversionServiceFactoryBean在Spring的IOC容器中定义一个ConversionService,Spring将自动识别出IOC容器中的ConversionService,并在Bean属性配置及SpringMVC处理方法入参绑定等场合使用它进行数据的转换。
可通过ConversionServiceFactoryBean的converters属性注册自定义的类型转换器。
Spring定义了3种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到ConversionServiceFactoryBean中:
Converter<S,T>
:将S类型对象转为T类型对象ConverterFactory
:将相同系列多个同质的Converter封装在一起,如果希望将一种类型的对象转换为另一种类型及其子类型的对象(例如将String转换为Number及Number子类)可使用转换器工厂类GenericConverter
:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换
<mvc:annotation-driven conversion-service="conversionService"/>
会将自定义的ConversionService注册到SpringMVC的上下文中,例如:
|
|
annotation-driven
配置
有下面几种情况需要使用annotation-driven
:
- 使用
<mvc:view-controller/>
配置直接转发的页面,无需经过Handler方法 - 使用
<mvc:default-servlet-handler/>
配置访问静态资源文件 - 在使用自定义类型转换器时使用
<mvc:annotation-driven/>
的conversion-service
属性
在开发过程中通常需要加入该配置。
<mvc:annotation-driven/>
会自动注册RequestMappingHandlerMapping、RequestMappingHandlerAdapter以及ExceptionHandlerExceptionResoler三个bean,还提供以下支持:
- 支持使用ConversionService实例对表单参数进行类型转换
- 支持使用
@NumberFormat
annotaion、@DateTimeFormat
注解完成数据类型的格式化 - 支持使用
@Valid
注解对JavaBean实例进行JSR 303验证 - 支持使用
@ResponseBody
和@RequestBody
注解
@InitBinder
- 由
@InitBinder
标识的方法,可以对WebDataBinder对象进行初始化,WebDataBinder是DataBinder的子类,用于完成由表单字段到JavaBean属性的绑定。 @InitBinder
方法不能有返回值,它必须声明为void@InitBinder
方法的参数值通常是WebDataBinder
例如:
|
|
数据的格式化
现在有这样一种情况,在Bean中有一个Date类型的对象,此时在表单中提交数据时会报错,因为SpringMVC无法知道你需要将什么样的数据转换为Date类型,此时需要在Bean的目标属性上面通过@DateTimeFormat
注解声明日期的格式,例如:
|
|
这个的前提是需要在SpringMVC的配置文件中配置:
|
|
另外一种情况,假设Bean中有一个float类型的属性:
|
|
现在在表单中输入1,234,567.8
,此时也会出错,所以此时也需要在目标属性上加注解:
|
|
Spring在格式化模块中定义了一个实现ConversionService接口的FormattingConversionService实现类,该实现类扩展了GenericConversionService,因此它既具有类型转换的功能,又具有格式化的功能。
FormattingConversionService拥有一个FormattingConversionServiceFactoryBean工厂类,后者用于在Spring上下文中构造前者。
FormattingConversionServiceFactoryBean内部已经注册了:
- NumberFormatAnnotationFormatterFactory:支持对数字类型的属性使用
@NumberFormat
注解 - JodaDateTimeFormatAnnotationFormatterFactory:支持对日期类型的属性
@DateTimeFormat
注解
装配了FormattingConversionServiceFactoryBean后,就可以在Spring MVC入参绑定及模型数据输出时使用注解驱动了。
<mvc:annotation-driven/>
默认创建的ConversionService实例即为FormattingConversionServiceFactoryBean。
数据校验
- JSR 303是Java为Bean数据合法性校验提供的标准框架,它已经包含在JavaEE6.0中
- JSR 303通过在Bean属性上标注类似于
@NotNull
、@Max
等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。 - Hibernate Validator是JSR 303的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:
@Email
被注解的元素必须是电子邮件地址@Length
被注解的字符串的大小必须在指定的范围内@NotEmpty
被注解的字符串必须非空@Range
被注解的元素必须在合适的范围内
- Spring4.0拥有自己的数据校验框架,同时支持JSR 303标准的校验框架
- Spring在进行数据绑定时,可同时调用校验框架完成数据校验工作,在SpringMVC中,可直接通过注解驱动的方式进行数据校验
- Spring的LocalValidatorFactoryBean既实现了Spring的Validator接口,也实现了JSR 303的Validator接口,只要在Spring容器里定义一个LocalValidatorFactoryBean,即可将其注入到需要数据校验的Bean中
- Spring本身并没有提供JSR 303的实现,所以需要将JSR 303的实现者的JAR包放在类路径下。
<mvc:annotation-driven>
会默认装配一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@valid
注解即可让SpringMVC在完成数据绑定后进行数据的校验- SpringMVC是通过对处理方法签名的规则来保存校验结果的:前一个表单/命令对象的校验结果保存在随后的入参中,这个保存校验结果的入参必须是BindingResult或Errors类型。
- 整个校验的流程如下:
- 使用JSR 303验证标准
- 加入hibernate validator验证框架的Jar包
- 在SpringMVC配置文件中加入
<mvc:annotation-driven/>
- 需要在bean的属性上添加对应的注解
- 在目标方法bean类型的前面添加
@Valid
注解
错误消息
显示错误消息
在jsp中通过
<form:errors/>
标签进行错误消息的显示,该标签有一个属性path
,若设置为*
,则显示所有的错误消息。当然我们可以将错误消息分别显示在对应字段的后面,此时在对应字段的后面加上
<form:errors/>
标签,其path属性写上对应字段的名字就可以了,例如:12LastName: <form:input path="lastName"/><form-errors path="lastName"></form-errors>定制错误消息
每个属性在数据绑定和数据校验发生错误时都会生成一个对应的FieldError对象。
当一个属性校验失败后,校验框架会为该属性生成4和消息代码,这些代码以校验注解类名为前缀,结合modelAttribute、属性名和属性类型名生成多个对应的消息代码,例如User类中的password属性标注了一个
@Pattern
注解,当该属性不满足@Pattern
所定义的规则时,就会产生下面4个消息代码:- Pattern.user.password
- Pattern.password
- Pattern.java.lang.String
- Pattern
当使用SpringMVC标签显示错误消息时,SpringMVC会查看WEB上下文是否装配了对应的国际化消息,如果没有,则显示默认的错误消息,否则使用国际化消息。